﻿using System;
using System.Reflection;

namespace PrivateAccessor
{
    public static class PrivateAccessorEx
    {
        #region Func
        public static object CallFunc(this object instance, string methodName, params object[] parameters)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return mi.Invoke(instance, parameters);
        }
        public static object CallFunc(this Type type, string methodName, params object[] parameters)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return mi.Invoke(null, parameters);
        }

        public static TReturn CallFunc<TReturn>(this object instance, string methodName)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return (TReturn)mi.Invoke(instance, null);
        }
        public static TReturn CallFunc<TReturn>(this Type type, string methodName)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return (TReturn)mi.Invoke(null, null);
        }

        public static TReturn CallFunc<T1, TReturn>(this object instance, string methodName, T1 p1)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return (TReturn)mi.Invoke(instance, new object[] { p1 });
        }
        public static TReturn CallFunc<T1, TReturn>(this Type type, string methodName, T1 p1)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return (TReturn)mi.Invoke(null, new object[] { p1 });
        }

        public static TReturn CallFunc<T1, T2, TReturn>(this object instance, string methodName, T1 p1, T2 p2)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return (TReturn)mi.Invoke(instance, new object[] { p1, p2 });
        }
        public static TReturn CallFunc<T1, T2, TReturn>(this Type type, string methodName, T1 p1, T2 p2)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return (TReturn)mi.Invoke(null, new object[] { p1, p2 });
        }

        public static TReturn CallFunc<T1, T2, T3, TReturn>(this object instance, string methodName, T1 p1, T2 p2, T3 p3)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return (TReturn)mi.Invoke(instance, new object[] { p1, p2, p3 });
        }
        public static TReturn CallFunc<T1, T2, T3, TReturn>(this Type type, string methodName, T1 p1, T2 p2, T3 p3)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return (TReturn)mi.Invoke(null, new object[] { p1, p2, p3 });
        }

        public static TReturn CallFunc<T1, T2, T3, T4, TReturn>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return (TReturn)mi.Invoke(instance, new object[] { p1, p2, p3, p4 });
        }
        public static TReturn CallFunc<T1, T2, T3, T4, TReturn>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return (TReturn)mi.Invoke(null, new object[] { p1, p2, p3, p4 });
        }

        public static TReturn CallFunc<T1, T2, T3, T4, T5, TReturn>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return (TReturn)mi.Invoke(instance, new object[] { p1, p2, p3, p4, p5 });
        }
        public static TReturn CallFunc<T1, T2, T3, T4, T5, TReturn>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return (TReturn)mi.Invoke(null, new object[] { p1, p2, p3, p4, p5 });
        }

        public static TReturn CallFunc<T1, T2, T3, T4, T5, T6, TReturn>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return (TReturn)mi.Invoke(instance, new object[] { p1, p2, p3, p4, p5, p6 });
        }
        public static TReturn CallFunc<T1, T2, T3, T4, T5, T6, TReturn>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return (TReturn)mi.Invoke(null, new object[] { p1, p2, p3, p4, p5, p6 });
        }

        public static TReturn CallFunc<T1, T2, T3, T4, T5, T6, T7, TReturn>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return (TReturn)mi.Invoke(instance, new object[] { p1, p2, p3, p4, p5, p6, p7 });
        }
        public static TReturn CallFunc<T1, T2, T3, T4, T5, T6, T7, TReturn>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return (TReturn)mi.Invoke(null, new object[] { p1, p2, p3, p4, p5, p6, p7 });
        }

        public static TReturn CallFunc<T1, T2, T3, T4, T5, T6, T7, T8, TReturn>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            return (TReturn)mi.Invoke(instance, new object[] { p1, p2, p3, p4, p5, p6, p7, p8 });
        }
        public static TReturn CallFunc<T1, T2, T3, T4, T5, T6, T7, T8, TReturn>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8)
        {
            MethodInfo mi = GetMethod(type, methodName);
            return (TReturn)mi.Invoke(null, new object[] { p1, p2, p3, p4, p5, p6, p7, p8 });
        }
        #endregion

        #region Action
        /*
        public static void CallAction(this object instance, string methodName, params object[] parameters)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, parameters);
        }
        public static void CallAction(this Type type, string methodName, params object[] parameters)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, parameters);
        }
        */

        public static void CallAction(this object instance, string methodName)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, null);
        }
        public static void CallAction(this Type type, string methodName)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, null);
        }

        public static void CallAction<T1>(this object instance, string methodName, T1 p1)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, new object[] { p1 });
        }
        public static void CallAction<T1>(this Type type, string methodName, T1 p1)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, new object[] { p1 });
        }

        public static void CallAction<T1, T2>(this object instance, string methodName, T1 p1, T2 p2)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, new object[] { p1, p2 });
        }
        public static void CallAction<T1, T2>(this Type type, string methodName, T1 p1, T2 p2)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, new object[] { p1, p2 });
        }

        public static void CallAction<T1, T2, T3>(this object instance, string methodName, T1 p1, T2 p2, T3 p3)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, new object[] { p1, p2, p3 });
        }
        public static void CallAction<T1, T2, T3>(this Type type, string methodName, T1 p1, T2 p2, T3 p3)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, new object[] { p1, p2, p3 });
        }

        public static void CallAction<T1, T2, T3, T4>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, new object[] { p1, p2, p3, p4 });
        }
        public static void CallAction<T1, T2, T3, T4>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, new object[] { p1, p2, p3, p4 });
        }

        public static void CallAction<T1, T2, T3, T4, T5>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, new object[] { p1, p2, p3, p4, p5 });
        }
        public static void CallAction<T1, T2, T3, T4, T5>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, new object[] { p1, p2, p3, p4, p5 });
        }

        public static void CallAction<T1, T2, T3, T4, T5, T6>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, new object[] { p1, p2, p3, p4, p5, p6 });
        }
        public static void CallAction<T1, T2, T3, T4, T5, T6>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, new object[] { p1, p2, p3, p4, p5, p6 });
        }

        public static void CallAction<T1, T2, T3, T4, T5, T6, T7>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, new object[] { p1, p2, p3, p4, p5, p6, p7 });
        }
        public static void CallAction<T1, T2, T3, T4, T5, T6, T7>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, new object[] { p1, p2, p3, p4, p5, p6, p7 });
        }

        public static void CallAction<T1, T2, T3, T4, T5, T6, T7, T8>(this object instance, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            mi.Invoke(instance, new object[] { p1, p2, p3, p4, p5, p6, p7, p8 });
        }
        public static void CallAction<T1, T2, T3, T4, T5, T6, T7, T8>(this Type type, string methodName, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8)
        {
            MethodInfo mi = GetMethod(type, methodName);
            mi.Invoke(null, new object[] { p1, p2, p3, p4, p5, p6, p7, p8 });
        }


        public static void CallAction<T1, T2, T3>(this object instance, string methodName, ref T1 p1, ref T2 p2, ref T3 p3)
        {
            MethodInfo mi = GetMethod(instance, methodName);
            var prms = new object[] {p1, p2, p3};
            mi.Invoke(instance, prms);
            p1 = (T1)prms[0];
            p2 = (T2)prms[1];
            p3 = (T3)prms[2];
        }

        #endregion

        #region Property
        public static TReturn GetPropertyValue<TReturn>(this object instance, string propertyName)
        {
            PropertyInfo pi = GetProperty(instance, propertyName);
            return (TReturn)pi.GetValue(instance, null);
        }
        public static TReturn GetPropertyValue<TReturn>(this Type type, string propertyName)
        {
            PropertyInfo pi = GetProperty(type, propertyName);
            return (TReturn)pi.GetValue(null, null);
        }

        public static void SetPropertyValue<T>(this object instance, string propertyName, T val)
        {
            PropertyInfo pi = GetProperty(instance, propertyName);
            pi.SetValue(instance, val, null);
        }
        public static void SetPropertyValue<T>(this Type type, string propertyName, T val)
        {
            PropertyInfo pi = GetProperty(type, propertyName);
            pi.SetValue(null, val, null);
        }
        #endregion

        #region Field
        public static TReturn GetFieldValue<TReturn>(this object instance, string fieldName)
        {
            FieldInfo fi = GetField(instance, fieldName);
            return (TReturn)fi.GetValue(instance);
        }
        public static TReturn GetFieldValue<TReturn>(this Type type, string fieldName)
        {
            FieldInfo fi = GetField(type, fieldName);
            return (TReturn)fi.GetValue(null);
        }

        public static void SetFieldValue<T>(this object instance, string fieldName, T val)
        {
            FieldInfo pi = GetField(instance, fieldName);
            pi.SetValue(instance, val);
        }
        public static void SetFieldValue<T>(this Type type, string fieldName, T val)
        {
            FieldInfo pi = GetField(type, fieldName);
            pi.SetValue(null, val);
        }
        #endregion

        private static MethodInfo GetMethod(object instance, string methodName)
        {
            if (instance == null)
                throw new ArgumentException("instance is required parameter", "instance");

            MethodInfo mi = GetMethod(instance.GetType(), methodName);
            if (mi == null)
                throw new Exception(string.Format("Method '{0}' does not exist in the type '{1}'", methodName, instance.GetType().Name));

            return mi;
        }
        private static MethodInfo GetMethod(Type type, string methodName)
        {
            MethodInfo mi = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
            if (mi == null)
                throw new Exception(string.Format("Method '{0}' does not exist in the type '{1}'", methodName, type.Name));

            return mi;
        }

        private static PropertyInfo GetProperty(object instance, string propertyName)
        {
            if (instance == null)
                throw new ArgumentException("instance is required parameter", "instance");

            PropertyInfo pi = GetProperty(instance.GetType(), propertyName);
            if (pi == null)
                throw new Exception(string.Format("Property '{0}' does not exist in the type '{1}'", propertyName, instance.GetType().Name));

            return pi;
        }
        private static PropertyInfo GetProperty(Type type, string propertyName)
        {
            PropertyInfo pi = type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
            if (pi == null)
                throw new Exception(string.Format("Property '{0}' does not exist in the type '{1}'", propertyName, type.Name));

            return pi;
        }

        private static FieldInfo GetField(object instance, string propertyName)
        {
            if (instance == null)
                throw new ArgumentException("instance is required parameter", "instance");

            FieldInfo fi = GetField(instance.GetType(), propertyName);
            if (fi == null)
                throw new Exception(string.Format("Property '{0}' does not exist in the type '{1}'", propertyName, instance.GetType().Name));

            return fi;
        }
        private static FieldInfo GetField(Type type, string fieldName)
        {
            FieldInfo fi = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
            if (fi == null)
                throw new Exception(string.Format("Field '{0}' does not exist in the type '{1}'", fieldName, type.Name));

            return fi;
        }
    }
}
